﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetX;
using NesEmulatorX.Factories;

namespace NesEmulatorX
{
	public class Disassembler
	{
		#region Constants

		public const string HEADER_TEXT = "NesEmulatorX - Disassembler - Richard Hughes (richard.j.hughes@hotmail.com)";

		#endregion

		#region Properties

		public StringBuilder CurrentCode
		{
			get { return _currentCode; }
			set { _currentCode = value; }
		}
		private StringBuilder _currentCode = new StringBuilder();

		#endregion

		#region Public Methods

		public string Disassemble(NESROM rom)
		{
			if (rom == null)
				throw new ArgumentNullException("rom");

			this.CurrentCode.Clear();

			this.WriteHeader(rom);

			this.NewLine();

			this.WritePRGROMPages(rom);

			this.NewLine();

			this.WriteCHRROMPages(rom);

			return this.CurrentCode.ToString();
		}

		#endregion

		#region Private Methods

		private void WriteHeader(NESROM rom)
		{
			this.Comment();
			this.CurrentCode.AppendFormatLine("{0}", Disassembler.HEADER_TEXT);

			this.Comment();
			this.CurrentCode.AppendFormatLine("Disassembled on {0:yyyy/MM/dd HH:mm:ss}", DateTimeFactory.DateTime);

			this.NewLine();

			this.Comment();
			this.CurrentCode.AppendFormatLine("Mapper Number: {0}", rom.MapperNumber);

			this.Comment();
			this.CurrentCode.AppendFormatLine("PRG-ROM Page Count: {0}", rom.PRGROMPageCount);

			this.Comment();
			this.CurrentCode.AppendFormatLine("CHR-ROM Page Count: {0}", rom.CHRROMPageCount);
		}

		private void WritePRGROMPages(NESROM rom)
		{
			for (int i = 0; i < rom.PRGROMPageCount; i++)
			{
				this.WritePRGROMPage(rom, i);
			}
		}

		private void WritePRGROMPage(NESROM rom, int page)
		{
			this.Comment();
			this.CurrentCode.AppendFormatLine("PRG-ROM Page: {0}", page);

			int pageStartAddress = rom.GetPRGROMPageStartAddress(page);

			for (int i = 0; i < NESEmulator.PRG_ROM_SIZE; i++)
			{
				int line = i + (NESEmulator.PRG_ROM_SIZE * page);
				this.CurrentCode.AppendFormatLine("; [0x{0:X8}]", line); // this will write the opcode and line number
			}

			this.NewLine();
		}

		private void WriteCHRROMPages(NESROM rom)
		{
			for (int i = 0; i < rom.CHRROMPageCount; i++)
			{
				this.WriteCHRROMPage(rom, i);
			}
		}

		private void WriteCHRROMPage(NESROM rom, int page)
		{
			this.Comment();
			this.CurrentCode.AppendFormatLine("CHR-ROM Page: {0}", page);

			int pageStartAddress = rom.GetCHRROMPageStartAddress(page);

			for (int i = 0; i < NESEmulator.CHR_ROM_SIZE; i++)
			{
				int line = i + (NESEmulator.PRG_ROM_SIZE * rom.PRGROMPageCount) + (NESEmulator.CHR_ROM_SIZE * page);
				this.CurrentCode.AppendFormatLine("; [0x{0:X8}]", line); // this will write the opcode and line number
			}

			this.NewLine();
		}

		private void Comment()
		{
			this.CurrentCode.Append("; ");
		}

		private void NewLine()
		{
			this.CurrentCode.AppendLine();
		}

		#endregion
	}
}
